package com.famiover.generator.utils;

import com.famiover.generator.generator.MybatisCodeGenerator;
import com.google.common.base.CaseFormat;
import com.google.common.base.Preconditions;
import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.*;
import org.mybatis.generator.config.Context;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;

public class Utils {

    public static String getBaseName(String baseEntity) {
        if (isEmpty(baseEntity)) {
            return baseEntity;
        }
        if (!baseEntity.contains(".")) {
            return baseEntity;
        }
        final int i = baseEntity.lastIndexOf(".");
        return baseEntity.substring(i + 1);
    }


    public static boolean containBase(String string) {
        if (isEmpty(string)) {
            return false;
        }
        return string.toLowerCase().contains("base");
    }


    public static boolean isEmpty(String baseEntity) {
        if (Objects.isNull(baseEntity)) {
            return true;
        }
        return baseEntity.length() == 0;
    }

    public static boolean isNotEmpty(String baseEntity) {
        return !isEmpty(baseEntity);
    }

    public static String getProperty(Context context, String property) {
        String value = context.getProperty(property);
        if (isEmpty(value)) {
            throw new RuntimeException("缺少属性:" + property);
        }
        return value;
    }

    public static String getPropertyDefault(Context context, String property, String defaultValue) {
        String value = context.getProperty(property);
        if (isEmpty(value)) {
            return defaultValue;
        }
        return value;
    }

    public static String getJavaExampleTpk(IntrospectedTable introspectedTable) {
        Context context = introspectedTable.getContext();
        String javaExampleTpk = getJavaExampleTpk(context);
        if (isEmpty(javaExampleTpk)) {
            return "";
        }
        String exampleType = introspectedTable.getExampleType();
        String lastWord = getLastWord(exampleType);
        return javaExampleTpk + "." + lastWord;
    }


    public static String getJavaExampleTpk(Context context) {
        try {
            return getProperty(context, "javaExampleTpk");
        } catch (Exception e) {
            return "";
        }
    }


    public static void addAuthor(JavaElement javaElement) {
        String username = System.getenv("USERNAME");
        if (isEmpty(username)) {
            username = "";
        }
        if (javaElement == null) {
            return;
        }
        javaElement.addJavaDocLine("/**");
        javaElement.addJavaDocLine("*");
        javaElement.addJavaDocLine("* @author " + username);
        javaElement.addJavaDocLine("*/");
    }

    public static boolean isExtend(Context context) {
        String value = getProperty(context, "extend");
        if (Utils.isEmpty(value)) {
            throw new NullPointerException("Extend value is null.");
        }
        return Objects.equals("true", value);
    }

    public static String getLastWord(String exampleFullName) {
        if (isEmpty(exampleFullName)) {
            return "";
        }
        int i = exampleFullName.lastIndexOf(".");
        return exampleFullName.substring(i + 1);
    }

    public static void selectByExampleSelectiveXML(Document document, IntrospectedTable introspectedTable) {
        XmlElement selectByExampleSelectiveEle = new XmlElement("select");
        selectByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.SelectByExampleSelective));
        selectByExampleSelectiveEle.addAttribute(new Attribute("resultMap", introspectedTable.getBaseResultMapId()));
        selectByExampleSelectiveEle.addAttribute(new Attribute("parameterType", introspectedTable.getExampleType()));
        selectByExampleSelectiveEle.addElement(new TextElement("select"));
        Utils.addIfForEachElement(selectByExampleSelectiveEle);
        selectByExampleSelectiveEle.addElement(new TextElement("from " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));
        selectByExampleSelectiveEle.addElement(getSelectByExampleSelectiveIncludeElement(introspectedTable));
        Utils.addGroupByElement(selectByExampleSelectiveEle);
        Utils.addOrderByElement(selectByExampleSelectiveEle);
        Utils.addStartElement(selectByExampleSelectiveEle);
        addElementWithBestPosition(document.getRootElement(), selectByExampleSelectiveEle);
    }

    public static void countByExampleSelectiveXMLFixBug(Document document, IntrospectedTable introspectedTable) {
        XmlElement selectByExampleSelectiveEle = new XmlElement("select");
        selectByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.CountByExampleSelective));
        selectByExampleSelectiveEle.addAttribute(new Attribute("resultType", BlogGeneratorConstant.CountReturnType));
        selectByExampleSelectiveEle.addAttribute(new Attribute("parameterType", introspectedTable.getExampleType()));
        selectByExampleSelectiveEle.addElement(new TextElement("select count(*) from ( select"));
        Utils.addIfForEachElement(selectByExampleSelectiveEle);
        selectByExampleSelectiveEle.addElement(new TextElement("from " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));
        selectByExampleSelectiveEle.addElement(getSelectByExampleSelectiveIncludeElement(introspectedTable));
        Utils.addGroupByElement(selectByExampleSelectiveEle);
        selectByExampleSelectiveEle.addElement(new TextElement(") Temp"));
        addElementWithBestPosition(document.getRootElement(), selectByExampleSelectiveEle);
    }


    public static void countByExampleSelectiveXML(Document document, IntrospectedTable introspectedTable) {
        XmlElement selectByExampleSelectiveEle = new XmlElement("select");
        selectByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.CountByExampleSelective));
        selectByExampleSelectiveEle.addAttribute(new Attribute("resultType", BlogGeneratorConstant.CountReturnType));
        selectByExampleSelectiveEle.addAttribute(new Attribute("parameterType", introspectedTable.getExampleType()));
        selectByExampleSelectiveEle.addElement(new TextElement("select count(*) "));
        selectByExampleSelectiveEle.addElement(new TextElement("from " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));
        selectByExampleSelectiveEle.addElement(getSelectByExampleSelectiveIncludeElement(introspectedTable));
        Utils.addGroupByElement(selectByExampleSelectiveEle);
        addElementWithBestPosition(document.getRootElement(), selectByExampleSelectiveEle);
    }


    public static Element getSelectByExampleSelectiveIncludeElement(IntrospectedTable introspectedTable) {
        XmlElement ifElement = new XmlElement("if");
        ifElement.addAttribute(new Attribute("test", "_parameter != null"));
        XmlElement includeElement = new XmlElement("include");
        includeElement.addAttribute(new Attribute("refid", introspectedTable.getExampleWhereClauseId()));
        ifElement.addElement(includeElement);
        return ifElement;
    }


    /**
     * 在最佳位置添加节点
     *
     * @param rootElement
     * @param element
     */
    public static void addElementWithBestPosition(XmlElement rootElement, XmlElement element) {
        String id = getIdFromElement(element);
        if (id == null) {
            rootElement.addElement(element);
        } else {
            List<Element> elements = rootElement.getElements();
            int index = -1;
            for (int i = 0; i < elements.size(); i++) {
                Element ele = elements.get(i);
                if (ele instanceof XmlElement) {
                    String eleId = getIdFromElement((XmlElement) ele);
                    if (eleId != null) {
                        if (eleId.startsWith(id)) {
                            if (index == -1) {
                                index = i;
                            }
                        } else if (id.startsWith(eleId)) {
                            index = i + 1;
                        }
                    }
                }
            }

            if (index == -1 || index >= elements.size()) {
                rootElement.addElement(element);
            } else {
                elements.add(index, element);
            }
        }
    }


    /**
     * 找出节点ID值
     *
     * @param element
     * @return
     */
    private static String getIdFromElement(XmlElement element) {
        for (Attribute attribute : element.getAttributes()) {
            if (attribute.getName().equals("id")) {
                return attribute.getValue();
            }
        }
        return null;
    }

    public static void addExampleField(Context context, TopLevelClass topLevelClass, IntrospectedTable introspectedTable, String name) {
        CommentGenerator commentGenerator = context.getCommentGenerator();
        Field field = new Field();
        field.setVisibility(JavaVisibility.PUBLIC);
        field.setType(FullyQualifiedJavaType.getNewListInstance());
        field.setName(name);
        field.setInitializationString("new ArrayList<String>()");
        commentGenerator.addFieldComment(field, introspectedTable);
        topLevelClass.addField(field);
        char c = name.charAt(0);
        String camel = Character.toUpperCase(c) + name.substring(1);
        Method method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setName("set" + camel);
        method.addParameter(new Parameter(FullyQualifiedJavaType.getNewListInstance(), name));
        method.addBodyLine("this." + name + "=" + name + ";");
        commentGenerator.addGeneralMethodComment(method, introspectedTable);
        topLevelClass.addMethod(method);
        method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(FullyQualifiedJavaType.getNewListInstance());
        method.setName("get" + camel);
        method.addBodyLine("return " + name + ";");
        commentGenerator.addGeneralMethodComment(method, introspectedTable);
        topLevelClass.addMethod(method);
    }


    public static void addMapperBatchMethod(Interface interfaze, IntrospectedTable introspectedTable, String methodName, Parameter... parameters) {
        // 设置需要导入的类
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(FullyQualifiedJavaType.getNewListInstance());
        importedTypes.add(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));

        Method ibsmethod = new Method();
        // 1.设置方法可见性
        ibsmethod.setVisibility(JavaVisibility.PUBLIC);
        // 2.设置返回值类型
        FullyQualifiedJavaType ibsreturnType = FullyQualifiedJavaType.getIntInstance();// int型
        ibsmethod.setReturnType(ibsreturnType);
        // 3.设置方法名
        ibsmethod.setName(methodName);
        // 4.设置参数列表
        for (Parameter parameter : parameters) {
            ibsmethod.addParameter(parameter);
        }
        interfaze.addImportedTypes(importedTypes);
        interfaze.addMethod(ibsmethod);
    }

    public static void addMapperDeleteOrUpdateMethod(Interface interfaze, IntrospectedTable introspectedTable, String methodName, Parameter... parameters) {
        Method ibsmethod = new Method();
        // 1.设置方法可见性
        ibsmethod.setVisibility(JavaVisibility.PUBLIC);
        // 2.设置返回值类型
        FullyQualifiedJavaType paramType = FullyQualifiedJavaType.getIntInstance();
        ibsmethod.setReturnType(paramType);
        // 3.设置方法名
        ibsmethod.setName(methodName);
        // 4.设置参数列表
        for (Parameter parameter : parameters) {
            ibsmethod.addParameter(parameter);
        }
        interfaze.addMethod(ibsmethod);
    }


    public static void addMapperSelectByPKMethod(Interface interfaze, IntrospectedTable introspectedTable, String methodName, Parameter... parameters) {
        Method ibsmethod = new Method();
        // 1.设置方法可见性
        ibsmethod.setVisibility(JavaVisibility.PUBLIC);
        // 2.设置返回值类型
        FullyQualifiedJavaType paramType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());
        ibsmethod.setReturnType(paramType);
        // 3.设置方法名
        ibsmethod.setName(methodName);
        // 4.设置参数列表
        for (Parameter parameter : parameters) {
            ibsmethod.addParameter(parameter);
        }
        interfaze.addMethod(ibsmethod);
    }


    public static void addMapperSelectSelectiveMethod(Interface interfaze, IntrospectedTable introspectedTable, String methodName, Parameter... parameters) {
        // 设置需要导入的类
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(FullyQualifiedJavaType.getNewListInstance());
        importedTypes.add(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));

        Method ibsmethod = new Method();
        // 1.设置方法可见性
        ibsmethod.setVisibility(JavaVisibility.PUBLIC);
        // 2.设置返回值类型
        FullyQualifiedJavaType paramType = FullyQualifiedJavaType.getNewListInstance();
        FullyQualifiedJavaType paramListType = MybatisCodeGenerator.getFullyQualifiedJavaType(introspectedTable);
        paramType.addTypeArgument(paramListType);
        ibsmethod.setReturnType(paramType);

        // 3.设置方法名
        ibsmethod.setName(methodName);
        // 4.设置参数列表
        for (Parameter parameter : parameters) {
            ibsmethod.addParameter(parameter);
        }
        interfaze.addImportedTypes(importedTypes);
        interfaze.addMethod(ibsmethod);
    }

    public static void addSelectByBatchIdsPrimaryKey(Document document, IntrospectedTable introspectedTable) {
        XmlElement selectByExampleSelectiveEle = new XmlElement("select");
        selectByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.SelectByBatchIdPrimaryKey));
        selectByExampleSelectiveEle.addAttribute(new Attribute("resultMap", introspectedTable.getBaseResultMapId()));
        selectByExampleSelectiveEle.addAttribute(new Attribute("parameterType", FullyQualifiedJavaType.getNewMapInstance().getFullyQualifiedName()));
        selectByExampleSelectiveEle.addElement(new TextElement("select"));
        Utils.addIfForEachElement(selectByExampleSelectiveEle);

        List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
        IntrospectedColumn introspectedColumn = primaryKeyColumns.get(0);
        String actualColumnName = introspectedColumn.getActualColumnName();
        String text = " where " + actualColumnName + " in ";
        selectByExampleSelectiveEle.addElement(new TextElement("from " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime() + text));
        XmlElement forEach = getIdForEach(introspectedTable, "ids", "id");
        selectByExampleSelectiveEle.addElement(forEach);
        document.getRootElement().addElement(selectByExampleSelectiveEle);
    }


    public static void addDeleteByBatchIdsPrimaryKey(Document document, IntrospectedTable introspectedTable) {
        XmlElement deleteXmlElement = new XmlElement("delete");
        deleteXmlElement.addAttribute(new Attribute("id", BlogGeneratorConstant.DeleteByBatchIdPrimaryKey));
        deleteXmlElement.addAttribute(new Attribute("parameterType", FullyQualifiedJavaType.getNewMapInstance().getFullyQualifiedName()));

        List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
        IntrospectedColumn introspectedColumn = primaryKeyColumns.get(0);
        String actualColumnName = introspectedColumn.getActualColumnName();
        String text = " where " + actualColumnName + " in ";
        deleteXmlElement.addElement(new TextElement("delete from " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime() + text));
        XmlElement forEach = getIdForEach(introspectedTable, "ids", "id");
        deleteXmlElement.addElement(forEach);
        document.getRootElement().addElement(deleteXmlElement);
    }


    public static XmlElement getIdForEach(IntrospectedTable introspectedTable, String items, String item) {
        XmlElement forEachXmlElement = new XmlElement("foreach");
        forEachXmlElement.addAttribute(new Attribute("collection", items));
        forEachXmlElement.addAttribute(new Attribute("item", item));
        forEachXmlElement.addAttribute(new Attribute("open", "("));
        forEachXmlElement.addAttribute(new Attribute("separator", ","));
        forEachXmlElement.addAttribute(new Attribute("close", ")"));

        List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
        IntrospectedColumn introspectedColumn = primaryKeyColumns.get(0);
        String parameterClause = getParameterClause(introspectedColumn, item);
        forEachXmlElement.addElement(new TextElement(parameterClause));
        return forEachXmlElement;
    }


    public static void addMapperCountSelectiveMethod(Interface interfaze, IntrospectedTable introspectedTable, String methodName, Parameter... parameters) {
        // 设置需要导入的类
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<>();
        importedTypes.add(FullyQualifiedJavaType.getNewListInstance());
        importedTypes.add(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));

        Method ibsmethod = new Method();
        // 1.设置方法可见性
        ibsmethod.setVisibility(JavaVisibility.PUBLIC);
        // 2.设置返回值类型
        FullyQualifiedJavaType paramType = new FullyQualifiedJavaType("java.lang.Long");
        ibsmethod.setReturnType(paramType);

        // 3.设置方法名
        ibsmethod.setName(methodName);
        // 4.设置参数列表
        for (Parameter parameter : parameters) {
            ibsmethod.addParameter(parameter);
        }
        interfaze.addImportedTypes(importedTypes);
        interfaze.addMethod(ibsmethod);
    }


    public static void addIfForEachElement(XmlElement element) {
        XmlElement chooseXmlElement = new XmlElement("choose");

        XmlElement whenXmlElement = new XmlElement("when");
        whenXmlElement.addAttribute(new Attribute("test", "columns==null or columns.size()==0"));
        whenXmlElement.addElement(new TextElement("<include refid=\"Base_Column_List\"/>"));
        chooseXmlElement.addElement(whenXmlElement);

        XmlElement otherwiseXmlElement = new XmlElement("otherwise");
        XmlElement forEachXmlElement = new XmlElement("foreach");
        forEachXmlElement.addAttribute(new Attribute("collection", "columns"));
        forEachXmlElement.addAttribute(new Attribute("item", "column"));
        forEachXmlElement.addAttribute(new Attribute("separator", ","));
        forEachXmlElement.addElement(new TextElement("${column}"));
        otherwiseXmlElement.addElement(forEachXmlElement);
        chooseXmlElement.addElement(otherwiseXmlElement);

        element.addElement(chooseXmlElement);
    }


    public static void addGroupByElement(XmlElement element) {
        XmlElement groupByIf = new XmlElement("if");
        groupByIf.addAttribute(new Attribute("test", "groups !=null and groups.size()>0"));
        groupByIf.addElement(new TextElement(" group by"));
        XmlElement foreach = new XmlElement("foreach");
        foreach.addAttribute(new Attribute("item", "item"));
        foreach.addAttribute(new Attribute("index", "index"));
        foreach.addAttribute(new Attribute("collection", "groups"));
        foreach.addAttribute(new Attribute("separator", ","));
        foreach.addElement(new TextElement("${item}"));
        groupByIf.addElement(foreach);

        XmlElement ifElement = new XmlElement("if");
        ifElement.addAttribute(new Attribute("test", "having != null"));
        ifElement.addElement(new TextElement("having ${having}"));
        groupByIf.addElement(ifElement);
        element.addElement(groupByIf);
    }


    public static void addOrderByElement(XmlElement element) {
        XmlElement ifElement = new XmlElement("if");
        ifElement.addAttribute(new Attribute("test", "orderByClause != null"));
        ifElement.addElement(new TextElement("order by ${orderByClause}"));
        element.addElement(ifElement);
    }


    public static void addStartElement(XmlElement element) {
        XmlElement ifXMLElement = new XmlElement("if");
        ifXMLElement.addAttribute(new Attribute("test", "start != null and start > -1"));
        ifXMLElement.addElement(new TextElement("limit ${start},${pageSize}"));
        element.addElement(ifXMLElement);
    }

    public static String getPKColumnName(IntrospectedTable introspectedTable) {
        IntrospectedColumn pkIntrospectedColumn = getPKIntrospectedColumn(introspectedTable);
        return pkIntrospectedColumn.getActualColumnName();
    }

    public static IntrospectedColumn getPKIntrospectedColumn(IntrospectedTable introspectedTable) {
        List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
        return primaryKeyColumns.get(0);
    }

    public static void addMapperExampleMethod(String methodName, String paramName, Interface interfaze, IntrospectedTable introspectedTable) {
        interfaze.addImportedType(MybatisCodeGenerator.getFullyQualifiedExampleType(introspectedTable));
        FullyQualifiedJavaType paramListType = MybatisCodeGenerator.getFullyQualifiedExampleType(introspectedTable);
        final Parameter parameter = new Parameter(paramListType, paramName);

        if (Objects.equals(methodName, BlogGeneratorConstant.CountByExampleSelective)) {
            Utils.addMapperCountSelectiveMethod(interfaze, introspectedTable, methodName, parameter);
        }

        if (Objects.equals(methodName, BlogGeneratorConstant.SelectByExampleSelective)) {
            Utils.addMapperSelectSelectiveMethod(interfaze, introspectedTable, methodName, parameter);
        }

        if (Objects.equals(methodName, BlogGeneratorConstant.DeleteByExample)) {
            Utils.addMapperDeleteOrUpdateMethod(interfaze, introspectedTable, methodName, parameter);
        }

        if (Objects.equals(methodName, BlogGeneratorConstant.CountByExample)) {
            Utils.addMapperDeleteOrUpdateMethod(interfaze, introspectedTable, methodName, parameter);
        }

        if (Objects.equals(methodName, BlogGeneratorConstant.UpdateByExample) || Objects.equals(methodName, BlogGeneratorConstant.UpdateByExampleSelective)) {
            interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));
            String annotationModel = "@Param(\"" + "record" + "\")";
            Parameter parameterModel = new Parameter(MybatisCodeGenerator.getFullyQualifiedJavaType(introspectedTable), "record", annotationModel);
            String annotationExample = "@Param(\"" + "example" + "\")";
            Parameter parameterExample = new Parameter(MybatisCodeGenerator.getFullyQualifiedExampleType(introspectedTable), "example", annotationExample);
            Utils.addMapperDeleteOrUpdateMethod(interfaze, introspectedTable, methodName, parameterModel, parameterExample);
        }

        if (Objects.equals(methodName, BlogGeneratorConstant.SelectByPrimaryKey)) {
            paramName = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, paramName);
            interfaze.addImportedType(new FullyQualifiedJavaType("org.apache.ibatis.annotations.Param"));
            String annotationModel = "@Param(\"" + paramName + "\")";
            Parameter parameterModel = new Parameter(Utils.getPKIntrospectedColumn(introspectedTable).getFullyQualifiedJavaType(), paramName, annotationModel);
            String annotationExample = "@Param(\"" + BlogGeneratorConstant.Columns + "\")";
            FullyQualifiedJavaType newListInstance = FullyQualifiedJavaType.getNewListInstance();
            newListInstance.addTypeArgument(FullyQualifiedJavaType.getStringInstance());
            Parameter parameterExample = new Parameter(newListInstance, BlogGeneratorConstant.Columns, annotationExample);
            Utils.addMapperSelectByPKMethod(interfaze, introspectedTable, methodName, parameterModel, parameterExample);
        }
    }


    public static void addMapperModelMethod(String methodName, String records, Interface interfaze, IntrospectedTable introspectedTable) {
        FullyQualifiedJavaType paramType = FullyQualifiedJavaType.getNewListInstance();
        FullyQualifiedJavaType paramListType = MybatisCodeGenerator.getFullyQualifiedJavaType(introspectedTable);
        paramType.addTypeArgument(paramListType);
        final Parameter parameter = new Parameter(paramType, records);
        Utils.addMapperBatchMethod(interfaze, introspectedTable, methodName, parameter);
    }


    public static void mapperJavaExtendBase(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        Context context = introspectedTable.getContext();
        if (!Utils.isExtend(context)) {
            return;
        }
        String modelPath = introspectedTable.getBaseRecordType();
        String modelName = Utils.getLastWord(modelPath);
        String exampleName = String.format("%sExample", modelName);
        String primaryKeyShortName = MybatisCodeGenerator.getPrimaryKeyShortName(introspectedTable);
        String baseMapperPath = Utils.getProperty(context, "baseMapperPath");
        final String baseName = Utils.getBaseName(baseMapperPath);
        String superInterface = String.format("%s<%s,%s,%s>", baseName, modelName, exampleName, primaryKeyShortName);
        FullyQualifiedJavaType fullyQualifiedJavaType = new FullyQualifiedJavaType(superInterface);
        interfaze.addSuperInterface(fullyQualifiedJavaType);

        FullyQualifiedJavaType qualifiedJavaType0 = new FullyQualifiedJavaType(introspectedTable.getExampleType());
        interfaze.addImportedType(qualifiedJavaType0);

        FullyQualifiedJavaType qualifiedJavaType1 = new FullyQualifiedJavaType(baseMapperPath);
        interfaze.addImportedType(qualifiedJavaType1);

        String mapperPath = Utils.getPropertyDefault(context, "mapperPath", "");
        if (Utils.isNotEmpty(mapperPath)) {
            FullyQualifiedJavaType qualifiedJavaType2 = new FullyQualifiedJavaType(mapperPath);
            interfaze.addImportedType(qualifiedJavaType2);
        }

        FullyQualifiedJavaType qualifiedJavaType3 = MybatisCodeGenerator.getPrimaryKeyFullyQualifiedJavaType(introspectedTable);
        Preconditions.checkNotNull(qualifiedJavaType3, "不能为空!");
        interfaze.addImportedType(qualifiedJavaType3);

        FullyQualifiedJavaType qualifiedJavaType4 = new FullyQualifiedJavaType(modelPath);
        interfaze.addImportedType(qualifiedJavaType4);
        interfaze.getMethods().clear();
    }


    public static String getBeforeWord(String exampleFullName) {
        int i = exampleFullName.lastIndexOf(".");
        return exampleFullName.substring(0, i + 1);
    }

    public static void addBatchInsertSelectiveXml(Document document, IntrospectedTable introspectedTable) {
        List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
        //获得要自增的列名
        String incrementField = introspectedTable.getTableConfiguration().getProperties().getProperty("incrementField");
        if (incrementField != null) {
            incrementField = incrementField.toUpperCase();
        }
        XmlElement javaPropertyAndDbType = new XmlElement("trim");
        javaPropertyAndDbType.addAttribute(new Attribute("prefix", " ("));
        javaPropertyAndDbType.addAttribute(new Attribute("suffix", ")"));
        javaPropertyAndDbType.addAttribute(new Attribute("suffixOverrides", ","));

        XmlElement insertBatchElement = new XmlElement("insert");
        insertBatchElement.addAttribute(new Attribute("id", BlogGeneratorConstant.InsertBatchSelective));
        insertBatchElement.addAttribute(new Attribute("parameterType", "java.util.List"));

        XmlElement trim1Element = new XmlElement("trim");
        trim1Element.addAttribute(new Attribute("prefix", "("));
        trim1Element.addAttribute(new Attribute("suffix", ")"));
        trim1Element.addAttribute(new Attribute("suffixOverrides", ","));
        for (IntrospectedColumn introspectedColumn : columns) {
            String columnName = introspectedColumn.getActualColumnName();
            if (!columnName.toUpperCase().equals(incrementField)) {//不是自增字段的才会出现在批量插入中
                XmlElement iftest = new XmlElement("if");
                iftest.addAttribute(new Attribute("test", "list[0]." + introspectedColumn.getJavaProperty() + "!=null"));
                iftest.addElement(new TextElement(columnName + ","));
                trim1Element.addElement(iftest);
                XmlElement trimiftest = new XmlElement("if");
                trimiftest.addAttribute(new Attribute("test", "item." + introspectedColumn.getJavaProperty() + "!=null"));
                trimiftest.addElement(new TextElement("#{item." + introspectedColumn.getJavaProperty() + ",jdbcType=" + introspectedColumn.getJdbcTypeName() + "},"));
                javaPropertyAndDbType.addElement(trimiftest);
            }
        }

        XmlElement foreachElement = new XmlElement("foreach");
        foreachElement.addAttribute(new Attribute("collection", "list"));
        foreachElement.addAttribute(new Attribute("index", "index"));
        foreachElement.addAttribute(new Attribute("item", "item"));
        foreachElement.addAttribute(new Attribute("separator", ","));
        insertBatchElement.addElement(new TextElement("insert into " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));
        insertBatchElement.addElement(trim1Element);
        insertBatchElement.addElement(new TextElement(" values "));
        foreachElement.addElement(javaPropertyAndDbType);
        insertBatchElement.addElement(foreachElement);
        document.getRootElement().addElement(insertBatchElement);
    }

    public static void addBatchUpdateSelectiveXml(Document document, IntrospectedTable introspectedTable) {
        List<IntrospectedColumn> columns = introspectedTable.getAllColumns();
        String keyColumn = introspectedTable.getPrimaryKeyColumns().get(0).getActualColumnName();

        XmlElement insertBatchElement = new XmlElement("update");
        insertBatchElement.addAttribute(new Attribute("id", BlogGeneratorConstant.UpdateBatchByPrimaryKeySelective));
        insertBatchElement.addAttribute(new Attribute("parameterType", "java.util.List"));

        XmlElement foreach = new XmlElement("foreach");
        foreach.addAttribute(new Attribute("collection", "list"));
        foreach.addAttribute(new Attribute("item", "item"));
        foreach.addAttribute(new Attribute("index", "index"));
        foreach.addAttribute(new Attribute("separator", ";"));

        foreach.addElement(new TextElement("update " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));

        XmlElement trim1Element = new XmlElement("set");
        for (IntrospectedColumn introspectedColumn : columns) {
            updateItem(introspectedColumn, keyColumn, trim1Element);
        }
        foreach.addElement(trim1Element);

        foreach.addElement(new TextElement("where "));
        int index = 0;
        for (IntrospectedColumn i : introspectedTable.getPrimaryKeyColumns()) {
            foreach.addElement(new TextElement((index > 0 ? " AND " : "") + i.getActualColumnName() + " = #{item." + i.getJavaProperty() + ",jdbcType=" + i.getJdbcTypeName() + "}"));
        }
        insertBatchElement.addElement(foreach);
        document.getRootElement().addElement(insertBatchElement);
    }

    private static void updateItem(IntrospectedColumn introspectedColumn, String keyColumn, XmlElement trim1Element) {
        String columnName = introspectedColumn.getActualColumnName();
        if (!columnName.toUpperCase().equalsIgnoreCase(keyColumn)) {
            XmlElement ifxml = new XmlElement("if");
            ifxml.addAttribute(new Attribute("test", "item." + introspectedColumn.getJavaProperty() + "!=null"));
            ifxml.addElement(new TextElement(columnName + "=#{item." + introspectedColumn.getJavaProperty() + ",jdbcType=" + introspectedColumn.getJdbcTypeName() + "},"));
            trim1Element.addElement(ifxml);
        }
    }

    private static String getTestCondition(IntrospectedColumn introspectedColumn) {
        String format = "example.columns.contains('%s')";
        return String.format(format, introspectedColumn.getActualColumnName(), introspectedColumn.getJavaProperty());
    }

    private static String getTestValue(IntrospectedColumn introspectedColumn, boolean flag) {
        String format = flag ? "%s = #{record.%s,jdbcType=%s}," : "%s = #{record.%s,jdbcType=%s}";
        return String.format(format, introspectedColumn.getActualColumnName(), introspectedColumn.getJavaProperty(), introspectedColumn.getJdbcTypeName());
    }


    private static String getTestValue(IntrospectedColumn introspectedColumn) {
        String format = "%s = #{record.%s,jdbcType=%s}";
        return String.format(format, introspectedColumn.getActualColumnName(), introspectedColumn.getJavaProperty(), introspectedColumn.getJdbcTypeName());
    }


    private static void updatePrimaryKeyItem(IntrospectedColumn introspectedColumn, String keyColumn, XmlElement setXmlElement) {
        String columnName = introspectedColumn.getActualColumnName();
        if (columnName.toLowerCase().equalsIgnoreCase(keyColumn)) {
            return;
        }
        XmlElement ifxml = new XmlElement("if");
        ifxml.addAttribute(new Attribute("test", getTestCondition(introspectedColumn)));
        ifxml.addElement(new TextElement(getTestValue(introspectedColumn, true)));
        setXmlElement.addElement(ifxml);
    }


    private static void updatePrimaryKeyItem(IntrospectedColumn introspectedColumn, XmlElement setXmlElement) {
        String columnName = introspectedColumn.getActualColumnName();
        XmlElement ifxml = new XmlElement("if");
        ifxml.addAttribute(new Attribute("test", getTestCondition(introspectedColumn)));
        ifxml.addElement(new TextElement(getTestValue(introspectedColumn, true)));
        setXmlElement.addElement(ifxml);
    }


    public static void addUpdateByPrimaryKeyXml(Document document, IntrospectedTable introspectedTable) {
        XmlElement updateByExampleSelectiveEle = new XmlElement("update");
        updateByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.UpdateByPrimaryKey));
        updateByExampleSelectiveEle.addAttribute(new Attribute("parameterType", FullyQualifiedJavaType.getNewMapInstance().getFullyQualifiedName()));
        updateByExampleSelectiveEle.addElement(new TextElement("update " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));

        String keyColumn = introspectedTable.getPrimaryKeyColumns().get(0).getActualColumnName();

        XmlElement setXmlElement = new XmlElement("set");
        updateByExampleSelectiveEle.addElement(setXmlElement);
        List<IntrospectedColumn> allColumns = introspectedTable.getAllColumns();
        for (IntrospectedColumn allColumn : allColumns) {
            updatePrimaryKeyItem(allColumn, keyColumn, setXmlElement);
        }
        updateByExampleSelectiveEle.addElement(new TextElement("where " + getTestValue(introspectedTable.getPrimaryKeyColumns().get(0), false)));
        document.getRootElement().addElement(updateByExampleSelectiveEle);
    }

    public static void addUpdateByColumnsSelectiveXml(Document document, IntrospectedTable introspectedTable) {
        XmlElement updateByExampleSelectiveEle = new XmlElement("update");
        updateByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.UpdateByPrimaryKeyColumnsSelective));
        updateByExampleSelectiveEle.addAttribute(new Attribute("parameterType", "map"));
        updateByExampleSelectiveEle.addElement(new TextElement("update " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));
        List<IntrospectedColumn> allColumns = introspectedTable.getAllColumns();
        XmlElement setXmlElement = new XmlElement("set");
        updateByExampleSelectiveEle.addElement(setXmlElement);
        IntrospectedColumn primaryKey = introspectedTable.getPrimaryKeyColumns().get(0);
        for (IntrospectedColumn allColumn : allColumns) {
            String actualColumnName = allColumn.getActualColumnName();
            String actualColumnName1 = primaryKey.getActualColumnName();
            if (actualColumnName.equals(actualColumnName1)) {
                continue;
            }
            updateColumnsPrimaryKeyItem(allColumn, setXmlElement);
        }
        updateByExampleSelectiveEle.addElement(new TextElement("where " + getTestValue(primaryKey, false)));
        document.getRootElement().addElement(updateByExampleSelectiveEle);
    }

    private static void updateColumnsPrimaryKeyItem(IntrospectedColumn introspectedColumn, XmlElement setXmlElement) {
        XmlElement chooseXmlElement = new XmlElement("choose");
        XmlElement whenXmlElement = new XmlElement("when");
        whenXmlElement.addAttribute(new Attribute("test", Utils.getTestCondition(introspectedColumn)));
        whenXmlElement.addElement(new TextElement(getTestValue(introspectedColumn, true)));
        chooseXmlElement.addElement(whenXmlElement);
        XmlElement otherwiseXmlElement = new XmlElement("otherwise");
        XmlElement ifXmlElement = new XmlElement("if");
        boolean jdbcCharacterColumn = introspectedColumn.isJdbcCharacterColumn();
        if (jdbcCharacterColumn) {
            String field = "record." + introspectedColumn.getJavaProperty();
            String format = "%s != null and %s != ''";
            ifXmlElement.addAttribute(new Attribute("test", String.format(format, field, field)));
        } else {
            ifXmlElement.addAttribute(new Attribute("test", String.format("record.%s !=null", introspectedColumn.getJavaProperty())));
        }
        ifXmlElement.addElement(new TextElement(getTestValue(introspectedColumn, true)));
        otherwiseXmlElement.addElement(ifXmlElement);
        chooseXmlElement.addElement(otherwiseXmlElement);

        setXmlElement.addElement(chooseXmlElement);
    }


    public static void addUpdateByExampleXml(Document document, IntrospectedTable introspectedTable) {
        XmlElement updateByExampleSelectiveEle = new XmlElement("update");
        updateByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.UpdateByExample));
        updateByExampleSelectiveEle.addAttribute(new Attribute("parameterType", "map"));
        updateByExampleSelectiveEle.addElement(new TextElement("update " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));
        XmlElement setXmlElement = new XmlElement("set");
        updateByExampleSelectiveEle.addElement(setXmlElement);
        List<IntrospectedColumn> allColumns = introspectedTable.getAllColumns();
        for (IntrospectedColumn allColumn : allColumns) {
            updatePrimaryKeyItem(allColumn, setXmlElement);
        }
        XmlElement ifxml = new XmlElement("if");
        ifxml.addAttribute(new Attribute("test", "_parameter != null"));
        XmlElement refidXmlElement = new XmlElement("include");
        refidXmlElement.addAttribute(new Attribute("refid", "Update_By_Example_Where_Clause"));
        ifxml.addElement(refidXmlElement);
        updateByExampleSelectiveEle.addElement(ifxml);
        document.getRootElement().addElement(updateByExampleSelectiveEle);
    }


    public static void addSelectByPrimaryKeySelectiveXml(Document document, IntrospectedTable introspectedTable) {
        XmlElement selectByExampleSelectiveEle = new XmlElement("select");
        selectByExampleSelectiveEle.addAttribute(new Attribute("id", BlogGeneratorConstant.SelectByPrimaryKey));
        selectByExampleSelectiveEle.addAttribute(new Attribute("resultMap", introspectedTable.getBaseResultMapId()));
        selectByExampleSelectiveEle.addAttribute(new Attribute("parameterType", FullyQualifiedJavaType.getNewMapInstance().getFullyQualifiedName()));
        selectByExampleSelectiveEle.addElement(new TextElement("select"));
        Utils.addIfForEachElement(selectByExampleSelectiveEle);
        selectByExampleSelectiveEle.addElement(new TextElement("from " + introspectedTable.getAliasedFullyQualifiedTableNameAtRuntime()));
        List<IntrospectedColumn> primaryKeyColumns = introspectedTable.getPrimaryKeyColumns();
        IntrospectedColumn introspectedColumn = primaryKeyColumns.get(0);
        String actualColumnName = introspectedColumn.getActualColumnName();
        String text = " where " + actualColumnName + " = ";
        String parameterClause = getParameterClause(introspectedColumn, "id");
        selectByExampleSelectiveEle.addElement(new TextElement(text + parameterClause));
        document.getRootElement().addElement(selectByExampleSelectiveEle);
    }


    private static String getParameterClause(IntrospectedColumn introspectedColumn, String toColumnName) {
        return "#{" +
                toColumnName +
                ",jdbcType=" +
                introspectedColumn.getJdbcTypeName() +
                '}';
    }

    /**
     * 字符串转驼峰
     * user_name -> userName
     *
     * @param str 字符串
     */
    public static String toLowerCamel(String str) {
        return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, str);
    }

    /**
     * 字符串转驼峰
     * user_name -> UserName
     *
     * @param str 字符串
     */
    public static String toUpperCamel(String str) {
        return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, str);
    }


    public static boolean isTrue(String str) {
        return "true".equals(str);
    }

    public static boolean isFalse(String str) {
        return "false".equals(str);
    }

    public static void updateExampleAddCriterionMethod(TopLevelClass topLevelClass, JavaVisibility javaVisibility) {
        List<InnerClass> innerClasses = topLevelClass.getInnerClasses();
        for (InnerClass innerClass : innerClasses) {
            String shortName = innerClass.getType().getShortName();
            if (Objects.equals("GeneratedCriteria", shortName)) {
                List<Method> methods = innerClass.getMethods();
                for (Method method : methods) {
                    String name = method.getName();
                    if (Objects.equals(name, "addCriterion")) {
                        method.setVisibility(javaVisibility);
                    }
                }
            }
        }
    }


    public static void modelAddAnnotation(TopLevelClass topLevelClass, String importedType, String annotation) {
        topLevelClass.addImportedType(importedType);
        topLevelClass.addAnnotation(annotation);
    }

}
